package com.bdqn.controller;

import com.bdqn.pojo.User;
import com.bdqn.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * IndexController
 *
 * @author LILIBO
 * @since 2023-12-18
 */
@Controller
public class IndexController {

    @Resource
    private UserService userService;

    /**
     * 引入StringRedisTemplate组件bean
     */
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // Redis中数据的key的前缀
    private final int EXCEPTION_COUNT = 5;
    private final String SHIRO_LOGIN_COUNT = "shiro_login_count_"; // 登录计数
    private final String SHIRO_IS_LOCK = "shiro_is_lock_"; // 锁定用户登录

    /**
     * 去登录页面
     */
    @GetMapping("/login")
    public String toLogin() {
        // 计数
        stringRedisTemplate.boundValueOps("toLogin").increment(1);
        return "login";
    }

    /**
     * 执行登录
     */
    @PostMapping("/login")
    public String doLogin(Model model, HttpSession session, String usrName, String usrPassword) {
        // 调用业务逻辑层完成登录逻辑
        /* User loginUser = userService.login(usrName, usrPassword);
        if (loginUser == null) {
            model.addAttribute("message", "用户名或密码错误，请重新登录！");
            return "login";
        }
        // 登录成功，将登录用户设置到Session会话
        session.setAttribute("loginUser", loginUser);
        // 计数
        stringRedisTemplate.boundValueOps("doLogin").increment(1);
        return "main"; */

        // 登录失败次数超过5次锁定1小时
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();

        // 通过页面传入的账号密码构建登录令牌
        UsernamePasswordToken token = new UsernamePasswordToken(usrName, usrPassword);
        // 获得Shiro登录主体对象
        Subject subject = SecurityUtils.getSubject();
        try {
            if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + usrName))) {
                stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName); // 清空登录计
                model.addAttribute("message", "该账号已锁定，1小时后再试！");
                return "login";
            }
            // 使用Shiro方式进行登录验证
            subject.login(token);
        } catch (UnknownAccountException | IncorrectCredentialsException e) {
            // 登录失败次数超过5次锁定1小时
            if (!"LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + usrName))) {
                if (e instanceof IncorrectCredentialsException) {
                    opsForValue.increment(SHIRO_LOGIN_COUNT + usrName, 1); // 登录失败次数+1
                } else {
                    model.addAttribute("message", "用户名或密码错误，登录失败！");
                    return "login";
                }
                // 登录失败次数大于5次
                if (Integer.parseInt(Objects.requireNonNull(opsForValue.get(SHIRO_LOGIN_COUNT + usrName))) >= EXCEPTION_COUNT) {
                    // 锁定该用户1小时
                    opsForValue.set(SHIRO_IS_LOCK + usrName, "LOCK", 1, TimeUnit.HOURS);
                    stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName); // 清空登录计数
                    model.addAttribute("message", "该账号已锁定，1小时后再试！");
                    return "login";
                } else {
                    model.addAttribute("message", "用户名或密码错误，登录失败！（" + (EXCEPTION_COUNT - Integer.parseInt(Objects.requireNonNull(opsForValue.get(SHIRO_LOGIN_COUNT + usrName)))) + "次后账号将锁定）");
                    return "login";
                }
            }
        } catch (LockedAccountException e) {
            model.addAttribute("message", "用户被禁用，登录失败！");
            return "login";
        } catch (AuthenticationException e) {
            model.addAttribute("message", "认证异常，登录失败！");
            return "login";
        }
        // 如果登录成功清空该用户的计数和锁
        stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName); // 清空登录计
        stringRedisTemplate.delete(SHIRO_IS_LOCK + usrName); // 清空锁

        // 获取登录的主体信息
        User loginUser = (User) subject.getPrincipal();
        // 登录成功，将登录用户设置到Session会话
        session.setAttribute("loginUser", loginUser);

        System.out.println("Web Session: " + session.getId());
        System.out.println("Shiro Session: " + subject.getSession().getId());
        System.out.println(session + " - " + subject.getSession());

        // 计数
        stringRedisTemplate.boundValueOps("doLogin").increment(1);
        return "redirect:/main";
    }

    /**
     * 去主页面
     */
    @GetMapping("/main")
    public String toMain() {
        return "main";
    }

    /**
     * 退出登录
     */
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        session.removeAttribute("loginUser");
        SecurityUtils.getSubject().logout(); // Shiro注销
        return "login";
    }

    /**
     * 没有权限跳转到403页面
     */
    @GetMapping(value = "/403")
    public String unauthorized() {
        return "403";
    }

}
